#include <avr/io.h>
#include <avr/interrupt.h>
#include "i2cbar.h"

#define USI_DATA         USIDR
#define USI_STATUS      USISR
#define USI_CONTROL     USICR
//#define USI_ADDRESS      0x30
volatile uint8_t USI_ADDRESS;


#define NONE          0
#define ACK_PR_RX      1
#define BYTE_RX        2
#define ACK_PR_TX      3
#define PR_ACK_TX      4
#define BYTE_TX        5


#define DDR_USI             DDRB
#define PORT_USI            PORTB
#define PIN_USI             PINB
#define PORT_USI_SDA        PORTB0
#define PORT_USI_SCL        PORTB2


volatile uint8_t COMM_STATUS = NONE;


void USI_init(uint8_t addr) {
  USI_ADDRESS = addr;
  // 2-wire mode; Hold SCL on start and overflow; ext. clock
  USI_CONTROL |= (1<<USIWM1) | (1<<USICS1);
  USI_STATUS = 0xf0;  // write 1 to clear flags, clear counter
  DDR_USI  &= ~(1<<PORT_USI_SDA);
  PORT_USI &= ~(1<<PORT_USI_SDA);
  DDR_USI  |=  (1<<PORT_USI_SCL);
  PORT_USI |=  (1<<PORT_USI_SCL);
  // startcondition interrupt enable
  USI_CONTROL |= (1<<USISIE);
}


SIGNAL(SIG_USI_START) {
  uint8_t tmpUSI_STATUS;
  tmpUSI_STATUS = USI_STATUS;
  COMM_STATUS = NONE;
  // Wait for SCL to go low to ensure the "Start Condition" has completed.
  // otherwise the counter will count the transition
  while ( (PIN_USI & (1<<PORT_USI_SCL)) );
  USI_STATUS = 0xf0; // write 1 to clear flags; clear counter
  // enable USI interrupt on overflow; SCL goes low on overflow
  USI_CONTROL |= (1<<USIOIE) | (1<<USIWM0);
}




SIGNAL(SIG_USI_OVERFLOW) {

  static uint8_t sendPtr=0;

  uint8_t BUF_USI_DATA = USI_DATA;

  switch(COMM_STATUS) {
  case NONE:
    if (((BUF_USI_DATA & 0xfe) >> 1) != USI_ADDRESS) {  // if not receiving my address
      // disable USI interrupt on overflow; disable SCL low on overflow
      USI_CONTROL &= ~((1<<USIOIE) | (1<<USIWM0));
    }
    else { // else address is mine
	  DDR_USI  |=  (1<<PORT_USI_SDA);
      USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
      if (BUF_USI_DATA & 0x01) COMM_STATUS = ACK_PR_TX; else COMM_STATUS = ACK_PR_RX;
	  /* Prepare for sending and receiving */
	  
	  sendPtr = 0;
	  
	  /* END of preparation */
    }
    break;
  case ACK_PR_RX:
    DDR_USI  &= ~(1<<PORT_USI_SDA);
    COMM_STATUS = BYTE_RX;
    break;
  case BYTE_RX:
    /* Save received byte here! ... = USI_DATA*/
	
	_ping_enabled = (BUF_USI_DATA & I2C_CMD_PING_ENABLE ? 1: 0);
	_irBar_enabled = (BUF_USI_DATA & I2C_CMD_IRBAR_ENABLE ? 1: 0);
	if (BUF_USI_DATA & I2C_CMD_LIGHT1_ENABLE) {
		led1On();
	}
	else {
		led1Off();
	}
	if (BUF_USI_DATA & I2C_CMD_LIGHT2_ENABLE) {
		led2On();
	}
	else {
		led2Off();
	}
	_buzzer_sound_nr = BUF_USI_DATA >> 4 & 0x07;
	if(!_buzzer_sound_nr)
		BUZZER_STOP();
	if(_buzzer_sound_nr > 5)
		BUZZER_START();
	
	/* END of receive */
    DDR_USI  |=  (1<<PORT_USI_SDA);
    USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
    COMM_STATUS = ACK_PR_RX;
    break;
  case ACK_PR_TX:
    /* Put first byte to transmit in buffer here! USI_DATA = ... */
	
	USI_DATA = ((INPUT_PORT_PIN & 0x03) ^ 0x03); // button states inverted. 1 means on
	sendPtr++;
	
	/* END of transmit first byte */
    PORT_USI |=  (1<<PORT_USI_SDA); // transparent for shifting data out
    COMM_STATUS = BYTE_TX;
    break;
  case PR_ACK_TX:
    if(BUF_USI_DATA & 0x01) {
      COMM_STATUS = NONE; // no ACK from master --> no more bytes to send
    }
    else {
      /* Put next byte to transmit in buffer here! USI_DATA = ... */

		if(sendPtr == 1) {
			USI_DATA = (uint8_t) pingDistance;
			sendPtr++;
		}
		else if (sendPtr < 8) {
			USI_DATA = (uint8_t) irBarValue[sendPtr-2];
			sendPtr++;
		}
		else
			sendPtr=0;
		

	  /* END of transmit more bytes */
      PORT_USI |=  (1<<PORT_USI_SDA); // transparent for shifting data out
      DDR_USI  |=  (1<<PORT_USI_SDA);
      COMM_STATUS = BYTE_TX;
    }
    break;
  case BYTE_TX:
    DDR_USI  &= ~(1<<PORT_USI_SDA);
    PORT_USI &= ~(1<<PORT_USI_SDA);
    USI_STATUS = 0x0e;  // reload counter for ACK, (SCL) high and back low
    COMM_STATUS = PR_ACK_TX;
    break;
  }
  USI_STATUS |= (1<<USIOIF); // clear overflowinterruptflag, this also releases SCL
}





